package com.sparrow.common.util;

import com.google.common.base.Preconditions;
import com.sparrow.common.reload.ReloadMonitor;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * PropertiesUtil 类用于加载和管理可重新加载的属性文件。
 * 提供缓存机制，并支持文件变更自动重新加载。
 * 使用ClassLoader.getResourceAsStream(...) 从根目录查找资源路径
 */
public class PropertiesUtil {

    private static final Logger LOGGER = Logger.getLogger(PropertiesUtil.class.getName());

    private static final ConcurrentHashMap<String, ReloadableProperties> CACHE = new ConcurrentHashMap<>();

    private static ConcurrentHashMap<String, List<PropKeyReloadListener>> keyReloadListeners = new ConcurrentHashMap<>();

    public static String getProperty(String key) {
        for (ReloadableProperties properties : CACHE.values()) {
            if (properties.containsKey(key)) {
                return properties.getProperty(key);
            }
        }
        return null;
    }

    public static String getPropertyOrDefault(String key, String defaultVal) {
        String val = getProperty(key);
        return val != null ? val : defaultVal;
    }

    public static Integer getPropertyAsIntOr(String key, int defaultVal) {
        Integer val = getPropertyAsInt(key);
        return val != null ? val : defaultVal;
    }

    public static Long getPropertyAsLongOr(String key, long defaultVal) {
        String property = getProperty(key);
        return property != null ? Long.parseLong(property) : defaultVal;
    }

    public static Integer getPropertyAsInt(String key) {
        String property = getProperty(key);
        return property != null ? Integer.parseInt(property) : null;
    }

    public static void load(String... configPaths) {
        for (String path : configPaths) {
            CACHE.computeIfAbsent(path, k -> {
                File file = FileUtil.getFile(path);
                Preconditions.checkArgument(file != null && file.exists(), "文件不存在: " + path);

                Path filePath = file.toPath();
                ReloadableProperties properties = new ReloadableProperties();
                properties.load(path);

                ReloadMonitor.getInstance().registerReloadPath(filePath, tarPath -> {
                    LOGGER.info("重新加载属性文件: " + tarPath);
                    properties.reload(path);
                });
                return properties;
            });
        }
    }

    /**
     * 可重新加载的属性类。
     */
    public static class ReloadableProperties extends Properties {

        public void load(String path) {
            try (InputStream is = PropertiesUtil.class.getClassLoader().getResourceAsStream(path)) {
                if (is == null) {
                    throw new IOException("无法找到属性文件: " + path);
                }
                this.clear();
                this.load(is);
            } catch (IOException e) {
                LOGGER.log(Level.SEVERE, "加载属性文件失败: " + path, e);
                throw new RuntimeException(e);
            }
        }

        public void reload(String path) {
            Map<String, String> beforeKeyVals = getKeyValMap();
            this.load(path);
            Map<String, String> keyValMap = getKeyValMap();
            //对比变化的key 调用监听器
            for (String key : keyValMap.keySet()) {
                String newValue = keyValMap.get(key);
                String oldValue = beforeKeyVals.get(key);
                if (!Objects.equals(newValue, oldValue)) {
                    callReloadListener(key);
                }
            }
            // 如果有 key 被删除了，也可以检测
            for (String key : beforeKeyVals.keySet()) {
                if (!keyValMap.containsKey(key)) {
                    callReloadListener(key);
                }
            }
        }

        private void callReloadListener(String key) {
            List<PropKeyReloadListener> reloadListeners = keyReloadListeners.get(key);
            if (CollectionUtil.isNotEmpty(reloadListeners)) {
                for (PropKeyReloadListener reloadListener : reloadListeners) {
                    reloadListener.onReload(key);
                }
            }
        }

        private Map<String, String> getKeyValMap() {
            Map<String, String> keyValues = new HashMap<>();
            Set<String> kes = stringPropertyNames();
            for (String key : kes) {
                String val = getProperty(key);
                keyValues.put(key, val);
            }
            return keyValues;
        }
    }

    public interface PropKeyReloadListener {
        void onReload(String key);
    }

    public static void registerReloadListener(String key, PropKeyReloadListener reloadListener) {
        List<PropKeyReloadListener> reloadListeners = keyReloadListeners.computeIfAbsent(key, k -> new CopyOnWriteArrayList<>());
        reloadListeners.add(reloadListener);
    }

    public static int autoReloadInt(String key, Consumer<Integer> setter, Integer defaultValue) {
        int value = getPropertyAsIntOr(key, defaultValue);
        setter.accept(value);
        registerReloadListener(key, k -> setter.accept(getPropertyAsIntOr(key, defaultValue)));
        return value;
    }

    public static long autoReloadLong(String key, Consumer<Long> setter, Long defaultValue) {
        long value = getPropertyAsLongOr(key, defaultValue);
        setter.accept(value);
        registerReloadListener(key, k -> setter.accept(getPropertyAsLongOr(key, defaultValue)));
        return value;
    }

    public static String autoReloadString(String key, Consumer<String> setter, String defaultValue) {
        String value = getPropertyOrDefault(key, defaultValue);
        setter.accept(value);
        registerReloadListener(key, k -> setter.accept(getPropertyOrDefault(key, defaultValue)));
        return value;
    }

}
